package top.lww0511.extra.interceptor;

import com.baomidou.mybatisplus.core.conditions.Condition;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import top.lww0511.extra.annotation.Many2Many;
import top.lww0511.extra.utils.MpExtraUtil;
import top.lww0511.extra.utils.SpringBeanFactoryUtils;

/**
 * @author lww
 */
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Slf4j
public class Many2ManyInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if (result == null) {
            return null;
        }
        if (result instanceof ArrayList) {
            ArrayList list = (ArrayList) result;
            for (Object o : list) {
                handleMany2ManyAnnotation(o);
            }
        } else {
            handleMany2ManyAnnotation(result);
        }
        return result;
    }

    @SneakyThrows
    private void handleMany2ManyAnnotation(Object o) {
        Class<?> aClass = o.getClass();
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Many2Many many2Many = field.getAnnotation(Many2Many.class);
            if (many2Many != null) {
                String self = many2Many.self();
                Object value = MpExtraUtil.getValue(o, self);

                String leftMid = many2Many.leftMid();
                Class<? extends BaseMapper> midMapper = many2Many.midMapper();
                BaseMapper middleMapper = SpringBeanFactoryUtils.getApplicationContext().getBean(midMapper);
                QueryWrapper<Object> midWarpper = Condition.create().eq(leftMid, value);
                List<Map<String, Object>> list = middleMapper.selectMaps(midWarpper);
                String rightMid = many2Many.rightMid();
                List<Object> ids = new ArrayList<>();
                list.forEach(e -> ids.add(e.get(rightMid)));

                String origin = many2Many.right();
                Class<? extends BaseMapper> mapper = many2Many.rightMapper();
                BaseMapper baseMapper = SpringBeanFactoryUtils.getApplicationContext().getBean(mapper);
                QueryWrapper<Object> baseWarpper = Condition.create().in(origin, ids);
                List res = baseMapper.selectList(baseWarpper);
                field.set(o, res);
            }
        }
    }

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
